From d3faf9badf52a00fb3c1da2bfbb49228ffca075a Mon Sep 17 00:00:00 2001 From: "kfraser@localhost.localdomain" Date: Thu, 19 Jul 2007 12:53:32 +0100 Subject: [PATCH] [host s3] Retrieve necessary sleep information from plain-text ACPI tables (FADT/FACS), and keep one hypercall remained for sleep notification. Signed-off-by: Ke Yu Signed-off-by: Kevin Tian Signed-off-by: Keir Fraser --- xen/arch/x86/acpi/boot.c | 93 +++++++++++++++++++++++++ xen/arch/x86/acpi/power.c | 108 ++++++++++++++++-------------- xen/arch/x86/acpi/suspend.c | 3 - xen/arch/x86/platform_hypercall.c | 14 +--- xen/drivers/acpi/tables.c | 3 + xen/include/asm-x86/acpi.h | 20 ++++-- xen/include/public/platform.h | 25 ++----- xen/include/xen/acpi.h | 1 + 8 files changed, 174 insertions(+), 93 deletions(-) diff --git a/xen/arch/x86/acpi/boot.c b/xen/arch/x86/acpi/boot.c index 76f329d372..be16765afb 100644 --- a/xen/arch/x86/acpi/boot.c +++ b/xen/arch/x86/acpi/boot.c @@ -369,6 +369,95 @@ static int __init acpi_parse_hpet(unsigned long phys, unsigned long size) extern u32 pmtmr_ioport; #endif +#ifdef CONFIG_ACPI_SLEEP +/* Get pm1x_cnt and pm1x_evt information for ACPI sleep */ +static int __init +acpi_fadt_parse_sleep_info(struct fadt_descriptor_rev2 *fadt) +{ + struct facs_descriptor_rev2 *facs = NULL; + uint64_t facs_pa; + + if (fadt->revision >= FADT2_REVISION_ID) { + /* Sanity check on FADT Rev. 2 */ + if ((fadt->xpm1a_cnt_blk.address_space_id != + ACPI_ADR_SPACE_SYSTEM_IO) || + (fadt->xpm1b_cnt_blk.address_space_id != + ACPI_ADR_SPACE_SYSTEM_IO) || + (fadt->xpm1a_evt_blk.address_space_id != + ACPI_ADR_SPACE_SYSTEM_IO) || + (fadt->xpm1b_evt_blk.address_space_id != + ACPI_ADR_SPACE_SYSTEM_IO)) + goto bad; + + acpi_sinfo.pm1a_cnt = (uint16_t)fadt->xpm1a_cnt_blk.address; + acpi_sinfo.pm1b_cnt = (uint16_t)fadt->xpm1b_cnt_blk.address; + acpi_sinfo.pm1a_evt = (uint16_t)fadt->xpm1a_evt_blk.address; + acpi_sinfo.pm1b_evt = (uint16_t)fadt->xpm1b_evt_blk.address; + } + + if (!acpi_sinfo.pm1a_cnt) + acpi_sinfo.pm1a_cnt = (uint16_t)fadt->V1_pm1a_cnt_blk; + if (!acpi_sinfo.pm1b_cnt) + acpi_sinfo.pm1b_cnt = (uint16_t)fadt->V1_pm1b_cnt_blk; + if (!acpi_sinfo.pm1a_evt) + acpi_sinfo.pm1a_evt = (uint16_t)fadt->V1_pm1a_evt_blk; + if (!acpi_sinfo.pm1b_evt) + acpi_sinfo.pm1b_evt = (uint16_t)fadt->V1_pm1b_evt_blk; + + /* Now FACS... */ + if (fadt->revision >= FADT2_REVISION_ID) + facs_pa = fadt->xfirmware_ctrl; + else + facs_pa = (uint64_t)fadt->V1_firmware_ctrl; + + facs = (struct facs_descriptor_rev2 *) + __acpi_map_table(facs_pa, sizeof(struct facs_descriptor_rev2)); + if (!facs) + goto bad; + + if (strncmp(facs->signature, "FACS", 4)) { + printk(KERN_ERR PREFIX "Invalid FACS signature %s\n", + facs->signature); + goto bad; + } + + if (facs->length < 24) { + printk(KERN_ERR PREFIX "Invalid FACS table length: 0x%x", + facs->length); + goto bad; + } + + if (facs->length < 64) + printk(KERN_WARNING PREFIX + "FACS is shorter than ACPI spec allow: 0x%x", + facs->length); + + if ((acpi_rsdp_rev < 2) || + (facs->length < 32)) { + acpi_sinfo.wakeup_vector = facs_pa + + offsetof(struct facs_descriptor_rev2, + firmware_waking_vector); + acpi_sinfo.vector_width = 32; + } else { + acpi_sinfo.wakeup_vector = facs_pa + + offsetof(struct facs_descriptor_rev2, + xfirmware_waking_vector); + acpi_sinfo.vector_width = 64; + } + + printk (KERN_INFO PREFIX + "ACPI SLEEP INFO: pm1x_cnt[%x,%x], pm1x_evt[%x,%x]\n" + " wakeup_vec[%"PRIx64"], vec_size[%x]\n", + acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt, + acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_cnt, + acpi_sinfo.wakeup_vector, acpi_sinfo.vector_width); + return 0; +bad: + memset(&acpi_sinfo, 0, sizeof(acpi_sinfo)); + return 0; +} +#endif + static int __init acpi_parse_fadt(unsigned long phys, unsigned long size) { struct fadt_descriptor_rev2 *fadt = NULL; @@ -413,6 +502,10 @@ static int __init acpi_parse_fadt(unsigned long phys, unsigned long size) printk(KERN_INFO PREFIX "PM-Timer IO Port: %#x\n", pmtmr_ioport); #endif + +#ifdef CONFIG_ACPI_SLEEP + acpi_fadt_parse_sleep_info(fadt); +#endif return 0; } diff --git a/xen/arch/x86/acpi/power.c b/xen/arch/x86/acpi/power.c index 121327895e..54ac729a89 100644 --- a/xen/arch/x86/acpi/power.c +++ b/xen/arch/x86/acpi/power.c @@ -28,20 +28,15 @@ #define pmprintk(_l, _f, _a...) printk(_l "" _f, ## _a ) -u8 sleep_states[ACPI_S_STATE_COUNT]; -DEFINE_SPINLOCK(pm_lock); +static char opt_acpi_sleep[20]; +string_param("acpi_sleep", opt_acpi_sleep); -struct acpi_sleep_info { - uint16_t pm1a_cnt; - uint16_t pm1b_cnt; - uint16_t pm1a_evt; - uint16_t pm1b_evt; - uint16_t pm1a_cnt_val; - uint16_t pm1b_cnt_val; - uint32_t sleep_state; -} acpi_sinfo; +static u8 sleep_states[ACPI_S_STATE_COUNT]; +static DEFINE_SPINLOCK(pm_lock); -extern void do_suspend_lowlevel(void); +struct acpi_sleep_info acpi_sinfo; + +void do_suspend_lowlevel(void); static char *acpi_states[ACPI_S_STATE_COUNT] = { @@ -50,10 +45,6 @@ static char *acpi_states[ACPI_S_STATE_COUNT] = [ACPI_STATE_S4] = "disk", }; -unsigned long acpi_video_flags; -unsigned long saved_videomode; - -/* XXX: Add suspend failure recover later */ static int device_power_down(void) { console_suspend(); @@ -100,8 +91,27 @@ static void thaw_domains(void) domain_unpause(d); } +static void acpi_sleep_prepare(u32 state) +{ + void *wakeup_vector_va; + + if ( state != ACPI_STATE_S3 ) + return; + + wakeup_vector_va = __acpi_map_table( + acpi_sinfo.wakeup_vector, sizeof(uint64_t)); + if (acpi_sinfo.vector_width == 32) + *(uint32_t *)wakeup_vector_va = + (uint32_t)bootsym_phys(wakeup_start); + else + *(uint64_t *)wakeup_vector_va = + (uint64_t)bootsym_phys(wakeup_start); +} + +static void acpi_sleep_post(u32 state) {} + /* Main interface to do xen specific suspend/resume */ -int enter_state(u32 state) +static int enter_state(u32 state) { unsigned long flags; int error; @@ -123,6 +133,8 @@ int enter_state(u32 state) pmprintk(XENLOG_INFO, "PM: Preparing system for %s sleep\n", acpi_states[state]); + acpi_sleep_prepare(state); + local_irq_save(flags); if ((error = device_power_down())) @@ -152,6 +164,8 @@ int enter_state(u32 state) Done: local_irq_restore(flags); + acpi_sleep_post(state); + if ( !hvm_cpu_up() ) BUG(); @@ -160,30 +174,6 @@ int enter_state(u32 state) return error; } -/* - * Xen just requires address of pm1x_cnt, and ACPI interpreter - * is still kept in dom0. Address of xen wakeup stub will be - * returned, and then dom0 writes that address to FACS. - */ -int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info) -{ - if (acpi_sinfo.pm1a_cnt) - pmprintk(XENLOG_WARNING, "Multiple setting on acpi sleep info\n"); - - acpi_sinfo.pm1a_cnt = info->pm1a_cnt_port; - acpi_sinfo.pm1b_cnt = info->pm1b_cnt_port; - acpi_sinfo.pm1a_evt = info->pm1a_evt_port; - acpi_sinfo.pm1b_evt = info->pm1b_evt_port; - info->xen_waking_vec = (uint64_t)bootsym_phys(wakeup_start); - - pmprintk(XENLOG_INFO, "pm1a[%x],pm1b[%x],pm1a_e[%x],pm1b_e[%x]" - "wake[%"PRIx64"]", - acpi_sinfo.pm1a_cnt, acpi_sinfo.pm1b_cnt, - acpi_sinfo.pm1a_evt, acpi_sinfo.pm1b_evt, - info->xen_waking_vec); - return 0; -} - /* * Dom0 issues this hypercall in place of writing pm1a_cnt. Xen then * takes over the control and put the system into sleep state really. @@ -197,20 +187,23 @@ int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info) */ int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep) { - if (!IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt) + if ( !IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt ) return -EPERM; /* Sanity check */ - if (acpi_sinfo.pm1b_cnt_val && - ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) & - ACPI_BITMASK_SLEEP_ENABLE)) + if ( acpi_sinfo.pm1b_cnt_val && + ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) & + ACPI_BITMASK_SLEEP_ENABLE) ) { pmprintk(XENLOG_ERR, "Mismatched pm1a/pm1b setting\n"); return -EINVAL; } + if ( sleep->flags ) + return -EINVAL; + /* Write #1 */ - if (!(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE)) + if ( !(sleep->pm1a_cnt_val & ACPI_BITMASK_SLEEP_ENABLE) ) { outw((u16)sleep->pm1a_cnt_val, acpi_sinfo.pm1a_cnt); if (acpi_sinfo.pm1b_cnt) @@ -222,8 +215,6 @@ int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep) acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val; acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val; acpi_sinfo.sleep_state = sleep->sleep_state; - acpi_video_flags = sleep->video_flags; - saved_videomode = sleep->video_mode; return enter_state(acpi_sinfo.sleep_state); } @@ -247,7 +238,7 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) outw((u16)acpi_sinfo.pm1a_cnt_val, acpi_sinfo.pm1a_cnt); if (acpi_sinfo.pm1b_cnt) outw((u16)acpi_sinfo.pm1b_cnt_val, acpi_sinfo.pm1b_cnt); - + /* Wait until we enter sleep state, and spin until we wake */ while (!acpi_get_wake_status()); return_ACPI_STATUS(AE_OK); @@ -255,12 +246,24 @@ acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state) static int __init acpi_sleep_init(void) { - int i = 0; + int i; + char *p = opt_acpi_sleep; + + while ( (p != NULL) && (*p != '\0') ) + { + if ( !strncmp(p, "s3_bios", 7) ) + acpi_video_flags |= 1; + if ( !strncmp(p, "s3_mode", 7) ) + acpi_video_flags |= 2; + p = strchr(p, ','); + if ( p != NULL ) + p += strspn(p, ", \t"); + } pmprintk(XENLOG_INFO, "ACPI (supports"); - for (i = 0; i < ACPI_S_STATE_COUNT; i++) + for ( i = 0; i < ACPI_S_STATE_COUNT; i++ ) { - if (i == ACPI_STATE_S3) + if ( i == ACPI_STATE_S3 ) { sleep_states[i] = 1; printk(" S%d", i); @@ -269,6 +272,7 @@ static int __init acpi_sleep_init(void) sleep_states[i] = 0; } printk(")\n"); + return 0; } __initcall(acpi_sleep_init); diff --git a/xen/arch/x86/acpi/suspend.c b/xen/arch/x86/acpi/suspend.c index 2825c076e7..812d644e9f 100644 --- a/xen/arch/x86/acpi/suspend.c +++ b/xen/arch/x86/acpi/suspend.c @@ -35,9 +35,6 @@ void save_rest_processor_state(void) rdmsrl(MSR_CSTAR, saved_cstar); rdmsrl(MSR_LSTAR, saved_lstar); #endif - - bootsym(video_flags) = acpi_video_flags; - bootsym(video_mode) = saved_videomode; } #define loaddebug(_v,_reg) \ diff --git a/xen/arch/x86/platform_hypercall.c b/xen/arch/x86/platform_hypercall.c index 485d093547..6d1f019343 100644 --- a/xen/arch/x86/platform_hypercall.c +++ b/xen/arch/x86/platform_hypercall.c @@ -248,21 +248,9 @@ ret_t do_platform_op(XEN_GUEST_HANDLE(xen_platform_op_t) u_xenpf_op) } break; -#if 0 - case XENPF_set_acpi_sleep: - { - ret = set_acpi_sleep_info(&op->u.set_acpi_sleep); - if (!ret && copy_to_guest(u_xenpf_op, op, 1)) - ret = -EFAULT; - } - break; - case XENPF_enter_acpi_sleep: - { ret = acpi_enter_sleep(&op->u.enter_acpi_sleep); - } - break; -#endif + break; default: ret = -ENOSYS; diff --git a/xen/drivers/acpi/tables.c b/xen/drivers/acpi/tables.c index 313e7e5320..2a38116d15 100644 --- a/xen/drivers/acpi/tables.c +++ b/xen/drivers/acpi/tables.c @@ -73,6 +73,7 @@ struct acpi_table_sdt { static unsigned long sdt_pa; /* Physical Address */ static unsigned long sdt_count; /* Table count */ +unsigned char acpi_rsdp_rev; static struct acpi_table_sdt sdt_entry[ACPI_MAX_TABLES] __initdata; @@ -598,6 +599,8 @@ int __init acpi_table_init(void) "RSDP (v%3.3d %6.6s ) @ 0x%p\n", rsdp->revision, rsdp->oem_id, (void *)rsdp_phys); + acpi_rsdp_rev = rsdp->revision; + if (rsdp->revision < 2) result = acpi_table_compute_checksum(rsdp, diff --git a/xen/include/asm-x86/acpi.h b/xen/include/asm-x86/acpi.h index 1e3a873b94..b72c009227 100644 --- a/xen/include/asm-x86/acpi.h +++ b/xen/include/asm-x86/acpi.h @@ -173,15 +173,25 @@ extern unsigned long acpi_wakeup_address; /* early initialization routine */ extern void acpi_reserve_bootmem(void); -extern unsigned long acpi_video_flags; -extern unsigned long saved_videomode; -struct xenpf_set_acpi_sleep; +extern struct acpi_sleep_info acpi_sinfo; +#define acpi_video_flags bootsym(video_flags) struct xenpf_enter_acpi_sleep; -extern int set_acpi_sleep_info(struct xenpf_set_acpi_sleep *info); extern int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep); extern int acpi_enter_state(u32 state); -#endif /*CONFIG_ACPI_SLEEP*/ +struct acpi_sleep_info { + uint16_t pm1a_cnt; + uint16_t pm1b_cnt; + uint16_t pm1a_evt; + uint16_t pm1b_evt; + uint16_t pm1a_cnt_val; + uint16_t pm1b_cnt_val; + uint32_t sleep_state; + uint64_t wakeup_vector; + uint32_t vector_width; +}; + +#endif /* CONFIG_ACPI_SLEEP */ extern u8 x86_acpiid_to_apicid[]; #define MAX_LOCAL_APIC 256 diff --git a/xen/include/public/platform.h b/xen/include/public/platform.h index 0e60fadbe5..d1a44835de 100644 --- a/xen/include/public/platform.h +++ b/xen/include/public/platform.h @@ -153,27 +153,13 @@ struct xenpf_firmware_info { typedef struct xenpf_firmware_info xenpf_firmware_info_t; DEFINE_XEN_GUEST_HANDLE(xenpf_firmware_info_t); -#define XENPF_set_acpi_sleep 51 -struct xenpf_set_acpi_sleep { - /* IN variables. */ - uint16_t pm1a_cnt_port; - uint16_t pm1b_cnt_port; - uint16_t pm1a_evt_port; - uint16_t pm1b_evt_port; - /* OUT variables */ - uint64_t xen_waking_vec; /* Tell dom0 to set FACS waking vector */ -}; -typedef struct xenpf_set_acpi_sleep xenpf_set_acpi_sleep_t; -DEFINE_XEN_GUEST_HANDLE(xenpf_set_acpi_sleep_t); - -#define XENPF_enter_acpi_sleep 52 +#define XENPF_enter_acpi_sleep 51 struct xenpf_enter_acpi_sleep { /* IN variables */ - uint16_t pm1a_cnt_val; - uint16_t pm1b_cnt_val; - uint32_t sleep_state; /* Which state to enter */ - uint32_t video_flags; /* S3_bios or s3_mode */ - uint32_t video_mode; /* Mode setting for s3_mode */ + uint16_t pm1a_cnt_val; /* PM1a control value. */ + uint16_t pm1b_cnt_val; /* PM1b control value. */ + uint32_t sleep_state; /* Which state to enter (Sn). */ + uint32_t flags; /* Must be zero. */ }; typedef struct xenpf_enter_acpi_sleep xenpf_enter_acpi_sleep_t; DEFINE_XEN_GUEST_HANDLE(xenpf_enter_acpi_sleep_t); @@ -189,7 +175,6 @@ struct xen_platform_op { struct xenpf_microcode_update microcode; struct xenpf_platform_quirk platform_quirk; struct xenpf_firmware_info firmware_info; - struct xenpf_set_acpi_sleep set_acpi_sleep; struct xenpf_enter_acpi_sleep enter_acpi_sleep; uint8_t pad[128]; } u; diff --git a/xen/include/xen/acpi.h b/xen/include/xen/acpi.h index b84f047651..3482d91391 100644 --- a/xen/include/xen/acpi.h +++ b/xen/include/xen/acpi.h @@ -534,5 +534,6 @@ static inline int acpi_get_pxm(acpi_handle handle) #endif extern int pnpacpi_disabled; +extern unsigned char acpi_rsdp_rev; #endif /*_LINUX_ACPI_H*/ -- 2.30.2